package shopping.utils;

import shopping.transional.Service;
import shopping.transional.Transaction;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TransactionUtil {
    public static <T> T proxy(T origin) {
        //如果origin对象的类，没有被注解为Service，表示不属于Service层，正常执行
        if (!origin.getClass().isAnnotationPresent(Service.class)) {
            return origin;
        } else {
            //被注解为Service，走动态代理逻辑
            return proxy0(origin);
        }
    }

    private static <T> T proxy0(T origin) {
        //进行动态代理
        Object o = Proxy.newProxyInstance(
                TransactionUtil.class.getClassLoader(),
                origin.getClass().getInterfaces(),
                //实现类
                new transInvocationHandler(origin)
        );
        //返回增强后的对象
        return (T) o;
    }

    private static class transInvocationHandler implements InvocationHandler {
        private Object o;

        public transInvocationHandler(Object o) {
            this.o = o;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if(needTransaction(method)){
                try {
                    //需要事务，开始走事务逻辑
                    DataSourceUtil.beginTransaction();
                    Object invoke = method.invoke(o, args);
                    DataSourceUtil.commitAndClose();
                    return invoke;
                }catch (Exception e){
                    //回滚
                    DataSourceUtil.rollbackAndClose();
                    throw new RuntimeException(e);
                }
            }else{
                //不需要事务，走正常的方法
                return method.invoke(o,args);
            }
        }

        /**
         * 判断方法是否需要进行事务
         * @param interfaceMethod   接口中的方法
         */
        public boolean needTransaction(Method interfaceMethod) {
            try {
                //获取实现类的方法
                Method method = o.getClass().getMethod(interfaceMethod.getName(), interfaceMethod.getParameterTypes());
                return method.isAnnotationPresent(Transaction.class);
            } catch (NoSuchMethodException e) {
                return false;
            }
        }
    }
}
